<html>

<head>
<title>Noise Water</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<script type="text/javascript" src="Scripts/glMatrix-0.9.5.min.js"></script>
<script type="text/javascript" src="Scripts/webgl-utils.js"></script>

<script id="shader-water-fs" type="x-shader/x-fragment">
  precision highp float;

  uniform sampler2D permTexture;
  uniform sampler2D gradTexture;
  uniform float time; // Used for texture animation

  varying vec3 vTextureCoord;
  varying vec3 vTransformedNormal;
  varying vec4 vPosition;
  
    uniform float uMaterialShininess;

    uniform vec3 uAmbientColor;

    uniform vec3 uPointLightingLocation;
    uniform vec3 uPointLightingSpecularColor;
    uniform vec3 uPointLightingDiffuseColor;
  /*
   * To create offsets of one texel and one half texel in the
   * texture lookup, we need to know the texture image size.
   */
  #define ONE 0.00390625
  #define ONEHALF 0.001953125
  // The numbers above are 1/256 and 0.5/256, change accordingly
  // if you change the code to use another perm/grad texture size.

  void simplex(const in vec4 P, out vec4 offset1, out vec4 offset2, out vec4 offset3) {
    vec4 offset0;
 
    vec3 isX = step( P.yzw, P.xxx );       
    offset0.x = dot( isX, vec3( 1.0 ) );
    offset0.yzw = 1.0 - isX;

    vec2 isY = step( P.zw, P.yy );
    offset0.y += dot( isY, vec2( 1.0 ) );
    offset0.zw += 1.0 - isY;
 
    float isZ = step( P.w, P.z );
    offset0.z += isZ;
    offset0.w += 1.0 - isZ;
        
    offset3 = clamp(   offset0, 0.0, 1.0 );
    offset2 = clamp( --offset0, 0.0, 1.0 );
    offset1 = clamp( --offset0, 0.0, 1.0 );
  }
  
 /*
  * 4D simplex noise. A lot faster than classic 4D noise, and better looking.
  */

  float snoise(const in vec4 P) {

    // The skewing and unskewing factors are hairy again for the 4D case
    // This is (sqrt(5.0)-1.0)/4.0
    #define F4 0.309016994375
    // This is (5.0-sqrt(5.0))/20.0
    #define G4 0.138196601125

    // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
    float s = (P.x + P.y + P.z + P.w) * F4; // Factor for 4D skewing
    vec4 Pi = floor(P + s);
    float t = (Pi.x + Pi.y + Pi.z + Pi.w) * G4;
    vec4 P0 = Pi - t; // Unskew the cell origin back to (x,y,z,w) space
    Pi = Pi * ONE + ONEHALF; // Integer part, scaled and offset for texture lookup

    vec4 Pf0 = P - P0;  // The x,y distances from the cell origin

    vec4 o1;
    vec4 o2;
    vec4 o3;
    simplex(Pf0, o1, o2, o3);  

    // Noise contribution from simplex origin
    float perm0xy = texture2D(permTexture, Pi.xy).a;
    float perm0zw = texture2D(permTexture, Pi.zw).a;
    vec4  grad0 = texture2D(gradTexture, vec2(perm0xy, perm0zw)).rgba * 5.0 - 1.0;
    float t0 = 0.6 - dot(Pf0, Pf0);
    float n0;
    if (t0 < 0.0) n0 = 0.0;
    else {
      t0 *= t0;
      n0 = t0 * t0 * dot(grad0, Pf0);
    }

    // Noise contribution from second corner
    vec4 Pf1 = Pf0 - o1 + G4;
    o1 = o1 * ONE;
    float perm1xy = texture2D(permTexture, Pi.xy + o1.xy).a;
    float perm1zw = texture2D(permTexture, Pi.zw + o1.zw).a;
    vec4  grad1 = texture2D(gradTexture, vec2(perm1xy, perm1zw)).rgba * 5.0 - 1.0;
    float t1 = 0.6 - dot(Pf1, Pf1);
    float n1;
    if (t1 < 0.0) n1 = 0.0;
    else {
      t1 *= t1;
      n1 = t1 * t1 * dot(grad1, Pf1);
    }
  
    // Noise contribution from third corner
    vec4 Pf2 = Pf0 - o2 + 2.0 * G4;
    o2 = o2 * ONE;
    float perm2xy = texture2D(permTexture, Pi.xy + o2.xy).a;
    float perm2zw = texture2D(permTexture, Pi.zw + o2.zw).a;
    vec4  grad2 = texture2D(gradTexture, vec2(perm2xy, perm2zw)).rgba * 5.0 - 1.0;
    float t2 = 0.6 - dot(Pf2, Pf2);
    float n2;
    if (t2 < 0.0) n2 = 0.0;
    else {
      t2 *= t2;
      n2 = t2 * t2 * dot(grad2, Pf2);
    }
  
    // Noise contribution from fourth corner
    vec4 Pf3 = Pf0 - o3 + 3.0 * G4;
    o3 = o3 * ONE;
    float perm3xy = texture2D(permTexture, Pi.xy + o3.xy).a;
    float perm3zw = texture2D(permTexture, Pi.zw + o3.zw).a;
    vec4  grad3 = texture2D(gradTexture, vec2(perm3xy, perm3zw)).rgba * 5.0 - 1.0;
    float t3 = 0.6 - dot(Pf3, Pf3);
    float n3;
    if (t3 < 0.0) n3 = 0.0;
    else {
      t3 *= t3;
      n3 = t3 * t3 * dot(grad3, Pf3);
    }
  
    // Noise contribution from last corner
    vec4 Pf4 = Pf0 - vec4(1.0-4.0*G4);
    float perm4xy = texture2D(permTexture, Pi.xy + vec2(ONE, ONE)).a;
    float perm4zw = texture2D(permTexture, Pi.zw + vec2(ONE, ONE)).a;
    vec4  grad4 = texture2D(gradTexture, vec2(perm4xy, perm4zw)).rgba * 5.0 - 1.0;
    float t4 = 0.6 - dot(Pf4, Pf4);
    float n4;
    if(t4 < 0.0) n4 = 0.0;
    else {
      t4 *= t4;
      n4 = t4 * t4 * dot(grad4, Pf4);
    }

    // Sum up and scale the result to cover the range [-1,1]
    return 27.0 * (n0 + n1 + n2 + n3 + n4);
  }

  // Ridged multifractal
  float ridge(float h, float offset) {
    h = abs(h);
    h = offset - h;
    h = h * h;
    return h;
  }

  float ridgedmf(vec4 p, float lacunarity, float gain, float offset, int octaves) {
        float sum = 0.0;
        float freq = 2.5, amp = 0.1;
        float prev = 2.0;
        for(int i=0; i<3; i++) {
          float noise = snoise(p*freq);
      float n = ridge(noise, offset);
          sum += n*amp*prev;
          prev = n;
          freq *= lacunarity;
          amp *= gain;
        }
        return sum;
  }

  float ridgedmfDefault(vec4 p, int octaves) {
    return ridgedmf(p, 3.0, 1.8, 1.0, octaves);
  }

  void main(void) {
  
  
    float red = ridgedmfDefault(vec4(vTextureCoord.xyz, time / 5.0), 2);
    float green = ridgedmfDefault(vec4(vTextureCoord.xyz, time / 5.0), 2);
    float blue = ridgedmfDefault(vec4(vTextureCoord.xyz, time / 5.0), 2);
	
	vec3 lightWeighting;
	vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);
	vec3 normal = normalize(vTransformedNormal);
	
	float specularLightWeighting = 0.0;
		vec3 eyeDirection = normalize(-vPosition.xyz);
		vec3 reflectionDirection = reflect(-lightDirection, normal);

		specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uMaterialShininess);
		
		float diffuseLightWeighting = max(dot(normal, lightDirection), 0.0);
		lightWeighting = uAmbientColor
			+ uPointLightingSpecularColor * specularLightWeighting
			+ uPointLightingDiffuseColor * diffuseLightWeighting;
        

	

    vec4 color = vec4(0, green, 1, 1);
	gl_FragColor = vec4(color.rgb * lightWeighting, color.a);
  }
</script>

<script id="shader-water-vs" type="x-shader/x-vertex">
  attribute vec3 aVertexPosition;
  attribute vec3 aTextureCoord;
  attribute vec3 aVertexNormal;

  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;
  uniform mat3 uNMatrix;
  uniform float time;

  varying vec3 vTextureCoord;
  varying vec3 vTransformedNormal;
  varying vec4 vPosition;


  void main(void) {
		vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
   		vPosition.xz *= 0.15;
   		//vPosition.y = clamp( tan(aVertexPosition.z)*1.5 + tan(aVertexPosition.x)*1.5 , 0.0, 1.0)*cos(aVertexPosition.x+aVertexPosition.z)*5.0;
   		//vPosition.y *= sin(time*2.0);
   		
		vPosition.y += (sin(time) * cos(vPosition.x*0.2) + cos(time) * sin(vPosition.z*0.2))*0.5;
   		vPosition.y += sin(time) * cos(vPosition.x*0.2) * sin(vPosition.z*0.2);
		
		vPosition.x -= (cos(time) * cos(vPosition.y));
   		vPosition.z += sin(time) * sin(vPosition.y);
   		

       // vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
        gl_Position = uPMatrix * vPosition;
        vTextureCoord = vPosition.xyz;
        vTransformedNormal = uNMatrix * aVertexNormal;
  }
</script>

<script type="text/javascript">

  var perm;
  var grad3;
  var grad4;
  	var planeObj = new Object();
	planeObj.loaded = false;
  function initData() {
    perm = new Uint8Array([151,160,137,91,90,15,
                                     131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
                                     190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
                                     88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
                                     77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
                                     102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
                                     135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
                                     5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
                                     223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
                                     129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
                                     251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
                                     49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
                                     138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
                                     151,160,137,91,90,15,
                                     131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
                                     190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
                                     88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
                                     77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
                                     102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
                                     135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
                                     5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
                                     223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
                                     129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
                                     251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
                                     49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
                                     138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
                                    ]);
    grad3 = new Int8Array([0,1,1,1,1,-1,1,-1,1,1,-1,-1,
                   1,0,1,1,1,-1,-1,1,1,-1,1,-1,
                   1,1,0,1,-1,0,-1,1,0,-1,-1,1,
                   1,0,-1,-1,0,-1,0,-1,1,0,1,1]);
    grad4 = new Int8Array([0,1,1,1, 0,1,1,-1, 0,1,-1,1, 0,1,-1,-1,
                   0,-1,1,1, 0,-1,1,-1, 0,-1,-1,1, 0,-1,-1,-1,
                   1,0,1,1, 1,0,1,-1, 1,0,-1,1, 1,0,-1,-1,
                   -1,0,1,1, -1,0,1,-1, -1,0,-1,1, -1,0,-1,-1,
                   1,1,0,1, 1,1,0,-1, 1,-1,0,1, 1,-1,0,-1,
                   -1,1,0,1, -1,1,0,-1, -1,-1,0,1, -1,-1,0,-1,
                   1,1,1,0, 1,1,-1,0, 1,-1,1,0, 1,-1,-1,0,
                   -1,1,1,0, -1,1,-1,0, -1,-1,1,0, -1,-1,-1,0]);
  }
        
  var gl;
  function initGL(canvas) {
    try {
      gl = canvas.getContext("experimental-webgl");
      gl.viewportWidth = canvas.width;
      gl.viewportHeight = canvas.height;
    } catch (e) { }
      if (!gl) {
        alert("Could not initialise WebGL, sorry :-(");
      }
    }

  function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
        if (!shaderScript) {
                return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
                if (k.nodeType == 3) {
                        str += k.textContent;
                }
                k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
                return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(shader));
                return null;
        }

        return shader;
  }

  var waterShaderProgram;
  function initShaders() {
    
    var fragmentWaterShader = getShader(gl, "shader-water-fs");
    var vertexWaterShader = getShader(gl, "shader-water-vs");

    waterShaderProgram = gl.createProgram();
    gl.attachShader(waterShaderProgram, vertexWaterShader);
    gl.attachShader(waterShaderProgram, fragmentWaterShader);
    gl.linkProgram(waterShaderProgram);

    if (!gl.getProgramParameter(waterShaderProgram, gl.LINK_STATUS)) {
      canvas3d.style.display = "none";
    }

    gl.useProgram(waterShaderProgram);

    waterShaderProgram.vertexPositionAttribute = gl.getAttribLocation(waterShaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(waterShaderProgram.vertexPositionAttribute);
	
	waterShaderProgram.vertexNormalAttribute = gl.getAttribLocation(waterShaderProgram, "aVertexNormal");
    gl.enableVertexAttribArray(waterShaderProgram.vertexNormalAttribute);

    waterShaderProgram.textureCoordAttribute = gl.getAttribLocation(waterShaderProgram, "aTextureCoord");
    gl.enableVertexAttribArray(waterShaderProgram.textureCoordAttribute);

    waterShaderProgram.pMatrixUniform = gl.getUniformLocation(waterShaderProgram, "uPMatrix");
    waterShaderProgram.mvMatrixUniform = gl.getUniformLocation(waterShaderProgram, "uMVMatrix");
	waterShaderProgram.nMatrixUniform = gl.getUniformLocation(waterShaderProgram, "uNMatrix");

    waterShaderProgram.permSampler = gl.getUniformLocation(waterShaderProgram, "permTexture");
    waterShaderProgram.gradSampler = gl.getUniformLocation(waterShaderProgram, "gradTexture");
	waterShaderProgram.materialShininessUniform = gl.getUniformLocation(waterShaderProgram, "uMaterialShininess");
    waterShaderProgram.ambientColorUniform = gl.getUniformLocation(waterShaderProgram, "uAmbientColor");
    waterShaderProgram.pointLightingLocationUniform = gl.getUniformLocation(waterShaderProgram, "uPointLightingLocation");
    waterShaderProgram.pointLightingSpecularColorUniform = gl.getUniformLocation(waterShaderProgram, "uPointLightingSpecularColor");
    waterShaderProgram.pointLightingDiffuseColorUniform = gl.getUniformLocation(waterShaderProgram, "uPointLightingDiffuseColor");

    waterShaderProgram.timeUniform = gl.getUniformLocation(waterShaderProgram, "time");
  }

  var permTexture;
  function initPermTexture() {
    var width = 256;
    var components = 4;
    var pixels = new Uint8Array(width * width * components);
    for(var i = 0; i < width; i++) {
      for(var j = 0; j < width; j++) {
        var offset = (i * width + j) * components;
        var value = perm[(j + perm[i]) & 0xff];
        pixels[offset] = grad3[(value & 0x0f) * 3 + 0] * 64 + 64;
        pixels[offset + 1] = grad3[(value & 0x0f) * 3 + 1] * 64 + 64;
        pixels[offset + 2] = grad3[(value & 0x0f) * 3 + 2] * 64 + 64;
        pixels[offset + 3] = value;
      }
    }
    gl.pixelStorei ( gl.UNPACK_ALIGNMENT, 1 );
    permTexture = gl.createTexture();
    
    gl.bindTexture(gl.TEXTURE_2D, permTexture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, width, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  }

  var gradTexture;
  function initGradTexture() {
    var width = 256;
    var components = 4;
    var pixels = new Uint8Array(width * width * components);
    for(var i = 0; i<width; i++) {
      for(var j = 0; j<width; j++) {
        var offset = (i * width + j) * components;
        var value = perm[(j+perm[i]) & 0xff];
        pixels[offset] = grad4[(value & 0x1f) * 4 + 0] * 64 + 64;   // Gradient x
        pixels[offset+1] = grad4[(value & 0x1f) * 4 + 1] * 64 + 64; // Gradient y
        pixels[offset+2] = grad4[(value & 0x1f) * 4 + 2] * 64 + 64; // Gradient z
        pixels[offset+3] = grad4[(value & 0x1f) * 4 + 3] * 64 + 64; // Gradient w
      }
    }
    gl.pixelStorei ( gl.UNPACK_ALIGNMENT, 1 );
    gradTexture = gl.createTexture();

    gl.bindTexture(gl.TEXTURE_2D, gradTexture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, width, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  }

  var mvMatrix = mat4.create();
  var mvMatrixStack = [];
  var pMatrix = mat4.create();

  function mvPushMatrix() {
    var copy = mat4.create();
    mat4.set(mvMatrix, copy);
    mvMatrixStack.push(copy);
  }

  function mvPopMatrix() {
    if (mvMatrixStack.length == 0) {
      throw "Invalid popMatrix!";
    }
    mvMatrix = mvMatrixStack.pop();
  }

  function setMatrixUniforms(shaderProgram) {
        gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
	
	        var normalMatrix = mat3.create();
        mat4.toInverseMat3(mvMatrix, normalMatrix);
        mat3.transpose(normalMatrix);
        gl.uniformMatrix3fv(shaderProgram.nMatrixUniform, false, normalMatrix);
  }
  
  	function degToRad(degrees) {
		return degrees * Math.PI / 180;
	}
	
	function loadObjects() {
	
		//load plane
		var requestPlane = new XMLHttpRequest();
		requestPlane.open("GET", "TesselatedPlane.json");
		requestPlane.onreadystatechange = function() {	
	
		if (requestPlane.readyState == 4){
			handleLoadedobject(JSON.parse(requestPlane.responseText), 0, "plane");
		}
		};
		requestPlane.send();
	}
	
	function handleLoadedobject(objectData, index, type) {
				
		var object; 
		
		if (type == "fur") 
			object = furObj[index];
		else
			object = planeObj;

						
		object.normalBuffer = gl.createBuffer();
		gl.bindBuffer(gl.ARRAY_BUFFER, object.normalBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(objectData.normals), gl.STATIC_DRAW);
		object.normalBuffer.itemSize = 3;
		object.normalBuffer.numItems = objectData.normals.length / 3;
		object.texBuffer = gl.createBuffer();
		gl.bindBuffer(gl.ARRAY_BUFFER, object.texBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(objectData.uvs), gl.STATIC_DRAW);
		object.texBuffer.itemSize = 2;
		object.texBuffer.numItems = objectData.uvs.length / 2;
		object.positionBuffer = gl.createBuffer();
		gl.bindBuffer(gl.ARRAY_BUFFER, object.positionBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(objectData.positions), gl.STATIC_DRAW);
		object.positionBuffer.itemSize = 3;
		object.positionBuffer.numItems = objectData.positions.length / 3;
		object.indexBuffer = gl.createBuffer();
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, object.indexBuffer);
		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(objectData.indices), gl.STATIC_DRAW);
		object.indexBuffer.itemSize = 1;
		object.indexBuffer.numItems = objectData.indices.length;
				
		object.loaded = true;
				
}
  
  

  var cubeVertexPositionBuffer;
  var cubeVertexTextureCoordBuffer;
  var cubeVertexNormalBuffer;
  var cubeVertexIndexBuffer;
  function initBuffers() {
    cubeVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
    vertices = [
      -5.0, -5.0,  1.0,
       5.0, -5.0,  1.0,
       5.0,  5.0,  1.0,
      -5.0,  5.0,  1.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    cubeVertexPositionBuffer.itemSize = 3;
    cubeVertexPositionBuffer.numItems = 12;

    cubeVertexTextureCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
    var textureCoords = [
      -5.0, -5.0,  1.0,
       5.0, -5.0,  1.0,
       5.0,  5.0,  1.0,
      -5.0,  5.0,  1.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
    cubeVertexTextureCoordBuffer.itemSize = 3;
    cubeVertexTextureCoordBuffer.numItems = 12;
	
	 cubeVertexNormalBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
        var vertexNormals = [
             0.0,  0.0,  1.0,
             0.0,  0.0,  1.0,
             0.0,  0.0,  1.0,
             0.0,  0.0,  1.0,
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), gl.STATIC_DRAW);
        cubeVertexNormalBuffer.itemSize = 3;
        cubeVertexNormalBuffer.numItems = 12;

    cubeVertexIndexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    var cubeVertexIndices = [0, 1, 2, 0, 2, 3];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
    cubeVertexIndexBuffer.itemSize = 1;
    cubeVertexIndexBuffer.numItems = 6;
  }

  var xRot = 0;
  var yRot = 0;
  var zRot = 0;
  function drawScene() {
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

   mat4.perspective(67, gl.viewportWidth / gl.viewportHeight, 0.1, 100000.0, pMatrix);
	if (!planeObj.loaded) return;
        
        mvPushMatrix();
		
    mat4.identity(mvMatrix);

    mat4.translate(mvMatrix, [0, -1.0, -5.0]);
		
		
        gl.useProgram(waterShaderProgram);		
		gl.uniform3f(waterShaderProgram.ambientColorUniform, 0.2, 0.2, 0.1);
		gl.uniform3f(waterShaderProgram.pointLightingLocationUniform, 0, 1.5, -7);
		gl.uniform3f(waterShaderProgram.pointLightingSpecularColorUniform, 0, 0, 0);
		gl.uniform3f(waterShaderProgram.pointLightingDiffuseColorUniform, 0.3, 0.8, 1.0);
		
		mat4.scale(mvMatrix, [1000,1000,1000]);
		gl.uniform1f(waterShaderProgram.timeUniform, in_out);

		gl.bindBuffer(gl.ARRAY_BUFFER, planeObj.positionBuffer);
		gl.vertexAttribPointer(waterShaderProgram.vertexPositionAttribute, planeObj.positionBuffer.itemSize, gl.FLOAT, false, 0, 0);
		
		gl.bindBuffer(gl.ARRAY_BUFFER, planeObj.normalBuffer);
        gl.vertexAttribPointer(waterShaderProgram.vertexNormalAttribute, planeObj.normalBuffer.itemSize, gl.FLOAT, false, 0, 0);
	
		gl.bindBuffer(gl.ARRAY_BUFFER, planeObj.texBuffer);
		gl.vertexAttribPointer(waterShaderProgram.textureCoordAttribute, planeObj.texBuffer.itemSize, gl.FLOAT, false, 0, 0);

		gl.activeTexture(gl.TEXTURE0);
		gl.bindTexture(gl.TEXTURE_2D, permTexture);
		gl.uniform1i(waterShaderProgram.permSampler, 0);

		gl.activeTexture(gl.TEXTURE1);
		gl.bindTexture(gl.TEXTURE_2D, gradTexture);
		gl.uniform1i(waterShaderProgram.gradSampler, 1);

 		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, planeObj.indexBuffer);
		setMatrixUniforms(waterShaderProgram);
   		gl.drawElements(gl.TRIANGLES, planeObj.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);    
        
        mvPopMatrix();
  }

  var lastTime = 0;
  var in_out = 0;
  function animate() {
    var dateTime = new Date();
    var timeNow = dateTime.getTime();
    if (lastTime != 0) {
      var elapsed = timeNow - lastTime;
      var seconds = timeNow / 1000.0;
      var interval = 65536.0;
      var mod_outer = seconds % (interval * 2.0);
      var mod_inner = seconds % interval;
      if(mod_outer > interval) { in_out = interval - mod_inner; }
      else { in_out = mod_outer; }
      gl.uniform1f(waterShaderProgram.timeUniform, in_out);
      var elapsed_scaled = elapsed / 10.0;
      zRot += (90 * elapsed_scaled) / 1000.0;
    }
    lastTime = timeNow;
  }

  var canvas;
  function webGLStart() {
    canvas = document.getElementById("experimental-webgl");
    initGL(canvas);
    if(gl) {
	  resize();
      initData();
      initShaders();
      initBuffers();

      initPermTexture();
      initGradTexture();
	  loadObjects();

      gl.clearColor(0.0, 0.0, 0.0, 1.0);
      gl.clearDepth(1.0);

      gl.enable(gl.DEPTH_TEST);
      gl.depthFunc(gl.LEQUAL);

      render();
    }
  }
  
  	function resize() {
		gl.viewportWidth = canvas.width = window.innerWidth;
		gl.viewportHeight = canvas.height = window.innerHeight;
	}

  function render() {
    window.requestAnimFrame(render, canvas);
    drawScene();
    animate();
  }

</script>

</head>

<body onload="webGLStart();" bgcolor="black">
  <center>
    <canvas id="experimental-webgl" width="640" height="480"></canvas>
  </center>
</body>

</html>